Skip to content

Conversation

@pmenzel
Copy link
Collaborator

@pmenzel Paul Menzel (pmenzel) commented Oct 16, 2026 •

Update/install with sudo bee update compiler-rt-23.1.3-1 picolibc-1.8.12-0.

$ ./scripts/smoke-tests/baremetal-arm-smoke-test
      running with /usr/bin/qemu-system-arm (QEMU emulator version 5.1.0)
OK    arm-none-eabi/armv6m_soft_nofp: crt0, libc, builtins, picolibc.ld, headers
OK    arm-none-eabi/armv7m_soft_nofp: crt0, libc, builtins, picolibc.ld, headers
OK    arm-none-eabi/armv7em_soft_fpv4_sp_d16: crt0, libc, builtins, picolibc.ld, headers
OK    arm-none-eabi/armv7em_hard_fpv4_sp_d16: crt0, libc, builtins, picolibc.ld, headers
OK    arm-none-eabi/armv8m.main_soft_fp: crt0, libc, builtins, picolibc.ld, headers
OK    arm-none-eabi/armv8m.main_hard_fp: crt0, libc, builtins, picolibc.ld, headers
OK    Cortex-M0+ (--target=arm-none-eabi -mcpu=cortex-m0plus) -> arm-none-eabi/armv6m_soft_nofp
OK    Cortex-M23 (--target=arm-none-eabi -mcpu=cortex-m23) -> arm-none-eabi/armv6m_soft_nofp
OK    thumbv6m triple (--target=thumbv6m-none-eabi) -> arm-none-eabi/armv6m_soft_nofp
OK    Cortex-M3 (--target=arm-none-eabi -mcpu=cortex-m3) -> arm-none-eabi/armv7m_soft_nofp
OK    Cortex-M4 soft (--target=arm-none-eabi -mcpu=cortex-m4 -mfloat-abi=soft) -> arm-none-eabi/armv7m_soft_nofp
OK    Cortex-M4 softfp (--target=arm-none-eabi -mcpu=cortex-m4) -> arm-none-eabi/armv7em_soft_fpv4_sp_d16
OK    Cortex-M4 hard (--target=arm-none-eabi -mcpu=cortex-m4 -mfloat-abi=hard) -> arm-none-eabi/armv7em_hard_fpv4_sp_d16
OK    armv7em-eabihf triple (--target=armv7em-none-eabihf) -> arm-none-eabi/armv7em_hard_fpv4_sp_d16
OK    thumbv7em-eabihf triple (--target=thumbv7em-none-eabihf) -> arm-none-eabi/armv7em_hard_fpv4_sp_d16
OK    Cortex-M7 softfp (--target=arm-none-eabi -mcpu=cortex-m7) -> arm-none-eabi/armv7em_soft_fpv4_sp_d16
OK    Cortex-M7 hard (--target=arm-none-eabi -mcpu=cortex-m7 -mfloat-abi=hard) -> arm-none-eabi/armv7em_hard_fpv4_sp_d16
OK    Cortex-M33 soft (--target=arm-none-eabi -mcpu=cortex-m33 -mfloat-abi=soft) -> arm-none-eabi/armv7m_soft_nofp
OK    Cortex-M33 softfp (--target=arm-none-eabi -mcpu=cortex-m33) -> arm-none-eabi/armv8m.main_soft_fp
OK    Cortex-M33 hard (--target=arm-none-eabi -mcpu=cortex-m33 -mfloat-abi=hard) -> arm-none-eabi/armv8m.main_hard_fp
OK    Cortex-M33 without FPU (--target=arm-none-eabi -mcpu=cortex-m33+nofp) -> arm-none-eabi/armv7m_soft_nofp
OK    Cortex-M55 softfp (--target=arm-none-eabi -mcpu=cortex-m55): no variant, -Wmissing-multilib
OK    Cortex-M55 hard (--target=arm-none-eabi -mcpu=cortex-m55 -mfloat-abi=hard): no variant, -Wmissing-multilib
OK    Cortex-M0+ runs on microbit with arm-none-eabi/armv6m_soft_nofp
OK    Cortex-M3 runs on mps2-an385 with arm-none-eabi/armv7m_soft_nofp
WARN  Cortex-M4 soft: linked, but not run: /usr/bin/qemu-system-arm has no mps2-an386 board
WARN  Cortex-M4 softfp: linked, but not run: /usr/bin/qemu-system-arm has no mps2-an386 board
WARN  Cortex-M4 hard: linked, but not run: /usr/bin/qemu-system-arm has no mps2-an386 board
WARN  thumbv7em-eabihf triple: linked, but not run: /usr/bin/qemu-system-arm has no mps2-an386 board
WARN  Cortex-M7 softfp: linked, but not run: /usr/bin/qemu-system-arm has no mps2-an500 board
WARN  Cortex-M7 hard: linked, but not run: /usr/bin/qemu-system-arm has no mps2-an500 board
OK    Cortex-M33 soft runs on mps2-an505 with arm-none-eabi/armv7m_soft_nofp
OK    Cortex-M33 softfp runs on mps2-an505 with arm-none-eabi/armv8m.main_soft_fp
OK    Cortex-M33 hard runs on mps2-an505 with arm-none-eabi/armv8m.main_hard_fp
OK    default crt0, libc and builtins from arm-none-eabi/armv7em_hard_fpv4_sp_d16
PASS (0 failed, 6 warned)

The standalone build only builds the runtimes for the host, so
/usr/lib/clang/23/lib contains nothing but linux/, and linking for a
bare-metal Arm target fails:

    $ clang --target=arm-none-eabi -mcpu=cortex-m4 -mfloat-abi=hard \
          -fuse-ld=lld -nostartfiles -nolibc -Wl,-e,_start -o t.elf t.c
    ld.lld: error: cannot open /usr/lib/clang/23/lib/arm-unknown-none-eabi/libclang_rt.builtins.a: No such file or directory
    clang: error: linker command failed with exit code 1 (use -v to see invocation)

Build the builtins a second time for six Cortex-M variants, named like
those of Arm’s LLVM Embedded Toolchain:

    armv6m_soft_nofp           Cortex-M0/M0+/M1/M23
    armv7m_soft_nofp           Cortex-M3, and any later core with
                               -mfloat-abi=soft
    armv7em_soft_fpv4_sp_d16   Cortex-M4/M7, -mfloat-abi=softfp (default)
    armv7em_hard_fpv4_sp_d16   Cortex-M4/M7, -mfloat-abi=hard
    armv8m.main_soft_fp        Cortex-M33/M35P, -mfloat-abi=softfp
    armv8m.main_hard_fp        Cortex-M33/M35P, -mfloat-abi=hard

They are installed into /usr/lib/clang-runtimes/arm-none-eabi/<variant>,
clang’s default bare-metal sysroot, together with a multilib.yaml there.
clang then picks the variant from -mcpu, -mfloat-abi and -mfpu, so
--target=arm-none-eabi, armv7em-none-eabihf and thumbv7em-none-eabihf
all work. With per-target directories in the resource directory instead,
the --target spelling would have to match the directory name, and
--target=arm-none-eabi -mfloat-abi=hard would silently link the
soft-float library.

The softfp variants are needed because clang defaults to
-mfloat-abi=softfp for a core with FPU. That code uses the FPU, but the
crt0 of a variant without FPU does not enable it, so with only the
soft-float variants, picolibc’s hello world built with
-mcpu=cortex-m33 faulted in QEMU on its first VFP instruction, a VLDR
(encoding ed9f 0b17):

    ARM fault: hardfault
    	LR:   0x100000b1
    	PC:   0x100001e2

The variants without FPU therefore require -mfpu=none, and a core
without a matching variant, for example the Cortex-M55, gets clang’s
-Wmissing-multilib warning instead of a library that crashes.

compiler-rt appends the normalized target to the library directory, but
clang only searches the variant’s lib directory, so move the archive up.

The C library comes from to be added picolibc.be0, which uses the same
variants.

Assisted-by: claude-opus-5.5 (Claude Code)
clang finds the compiler-rt builtins for bare-metal Arm now, but there
is no C library for these targets:

    $ echo '#include <stdio.h>' | clang --target=arm-none-eabi -mcpu=cortex-m4 -x c -c - -o /dev/null
    <stdin>:1:10: fatal error: 'stdio.h' file not found

picolibc is the C library for embedded systems that Arm’s LLVM Embedded
Toolchain uses. Build it for the six Cortex-M variants of
compiler-rt.be0, into the same directories below
/usr/lib/clang-runtimes/arm-none-eabi, so that clang’s multilib.yaml
selects both together.

Each variant gets its own meson cross file, as picolibc’s own
cross-clang-*.txt files call arm-none-eabi-ar and link against libgcc
from an arm-none-eabi-gcc installation. Deviations from them:

- -nostdlibinc, so that the headers of an installed picolibc do not
  shadow the ones being built.
- c_ld = 'lld', as clang’s bare-metal driver defaults to GNU ld, which
  on MarIuX only supports x86:

      ERROR: Unable to detect linker for compiler `clang --target=thumbv6m-none-eabi -mfloat-abi=soft -nostdlibinc -nostdlib -Wl,--version`
      stderr: /usr/bin/ld: unrecognised emulation mode: armelf

- No -fshort-enums. picolibc passes it to match arm-none-eabi-gcc, but
  clang defaults to 32-bit enums, and the library has to match the code
  compiled with clang that links against it.

-Dspecsdir=none, as picolibc would otherwise install picolibc.specs into
clang’s install directory, /usr/bin.

Linked with the defaults (crt0-semihost.o, picolibc.ld, -lsemihost,
-fuse-ld=lld), a hello world doing 64-bit division and float/double
printf runs in QEMU 11.1.0 for every variant:

    -mcpu=cortex-m0plus                microbit      armv6m_soft_nofp
    -mcpu=cortex-m3                    mps2-an385    armv7m_soft_nofp
    -mcpu=cortex-m4 -mfloat-abi=soft   mps2-an386    armv7m_soft_nofp
    -mcpu=cortex-m4                    mps2-an386    armv7em_soft_fpv4_sp_d16
    -mcpu=cortex-m4 -mfloat-abi=hard   mps2-an386    armv7em_hard_fpv4_sp_d16
    --target=thumbv7em-none-eabihf     mps2-an386    armv7em_hard_fpv4_sp_d16
    -mcpu=cortex-m7                    mps2-an500    armv7em_soft_fpv4_sp_d16
    -mcpu=cortex-m7 -mfloat-abi=hard   mps2-an500    armv7em_hard_fpv4_sp_d16
    -mcpu=cortex-m33                   mps2-an505    armv8m.main_soft_fp
    -mcpu=cortex-m33 -mfloat-abi=hard  mps2-an505    armv8m.main_hard_fp
    -mcpu=cortex-m33 -mfloat-abi=soft  mps2-an505    armv7m_soft_nofp

    hello: 176366841446 6.250 0.333333

Assisted-by: claude-opus-5.5 (Claude Code)
compiler-rt.be0 and picolibc.be0 build the same six Cortex-M variants
into /usr/lib/clang-runtimes, and multilib.yaml lets clang pick one. A
wrong pick still links and only fails on the hardware, so check the
selection for 17 combinations of core, float ABI and target spelling,
the files of every variant, and run a program doing 64-bit division and
float and double printf for eleven of them in QEMU with semihosting.
Like llvm-smoke-test, --prefix mounts packages over /usr in a user
namespace, so they are tested before they are installed.

With the packages from this branch:

    $ TMPDIR=/dev/shm scripts/smoke-tests/baremetal-arm-smoke-test \
          --qemu /pkg/qemu-11.1.0-0/bin/qemu-system-arm \
          --prefix compiler-rt-23.1.3-1.x86_64.bee.tar.bz2 \
          --prefix picolibc-1.8.12-0.x86_64.bee.tar.bz2
    […]
    OK    Cortex-M33 softfp runs on mps2-an505 with arm-none-eabi/armv8m.main_soft_fp
    OK    Cortex-M33 hard runs on mps2-an505 with arm-none-eabi/armv8m.main_hard_fp
    OK    default crt0, libc and builtins from arm-none-eabi/armv7em_hard_fpv4_sp_d16
    PASS (0 failed, 0 warned)

The QEMU 5.1.0 in /usr/bin has no Cortex-M4 or Cortex-M7 board, so with
it those six programs are only linked, and reported as WARN.

With the first draft of multilib.yaml, which had no softfp variants and
did not require -mfpu=none for the variants without FPU, it fails:

    FAIL  Cortex-M4 softfp (--target=arm-none-eabi -mcpu=cortex-m4): expected arm-none-eabi/armv7em_soft_fpv4_sp_d16, got 'arm-none-eabi/armv7m_soft_nofp'
    […]
    FAIL  Cortex-M4 softfp on mps2-an386: exit status 1: ARM fault: hardfault
    FAIL  Cortex-M7 softfp on mps2-an500: exit status 1: ARM fault: hardfault
    FAIL  Cortex-M33 softfp on mps2-an505: exit status 1: ARM fault: hardfault
    FAIL (7 failed, 0 warned)

Every link passes -fuse-ld=lld. For bare-metal targets clang defaults
to the GNU ld in /usr/bin, which on MarIuX only knows x86:

    $ clang --target=arm-none-eabi -mcpu=cortex-m3 -T picolibc.ld \
          -o ld.elf hello.c -lsemihost
    /usr/bin/ld: unrecognised emulation mode: armelf

That is clang’s configuration, not a fault of the runtimes, so it is
not checked. CLANG_DEFAULT_LINKER would change the host’s linker too;
clang configuration files per target triple are the better candidate.

Two things found on the way: an automount such as /pkg/qemu-11.1.0-0
cannot be triggered from within the user namespace ("Too many levels of
symbolic links"), so the script runs QEMU once before entering it; and
QEMU 11 writes the semihosting console to stderr unless that is a tty.

Assisted-by: claude-opus-5.5 (Claude Code)
For --prefix, the smoke tests re-run themselves in a mount namespace
made with `unshare --user --map-root-user --mount`, and unshare(1)
defaults to private mount propagation. An automount triggered from
inside, such as a /pkg package not yet mounted, is then mounted by the
daemon in the host’s namespace and never shows up in the private copy:

    $ unshare --user --map-root-user --mount ls /pkg/boost-1.86.0-0
    ls: cannot open directory '/pkg/boost-1.86.0-0': Too many levels of symbolic links

So pass `--propagation slave`: host mounts, the automounts included,
then reach the namespace, while the overlay over /usr and every other
mount made inside still do not leak out to the host.

    $ unshare --user --map-root-user --mount --propagation slave \
          ls /pkg/perl-5.34.0-0/profile
    /pkg/perl-5.34.0-0/profile

This makes running QEMU once before re-executing, which
baremetal-arm-smoke-test did to mount /pkg/qemu-11.1.0-0, otiose, so
drop it. Now every /pkg path works, not only QEMU’s.

Host mount changes now also reach the namespace, among them the autofs
expiry after timeout=300, but the next access simply mounts it again.

With /pkg/qemu-11.1.0-0 not mounted beforehand:

    $ TMPDIR=/dev/shm scripts/smoke-tests/baremetal-arm-smoke-test \
          --qemu /pkg/qemu-11.1.0-0/bin/qemu-system-arm \
          --prefix compiler-rt-23.1.3-1.x86_64.bee.tar.bz2 \
          --prefix picolibc-1.8.12-0.x86_64.bee.tar.bz2
          testing, over /usr: /dev/shm/baremetal-arm-smoke-d9n6vdgy/0 /dev/shm/baremetal-arm-smoke-d9n6vdgy/1
          running with /pkg/qemu-11.1.0-0/bin/qemu-system-arm (QEMU emulator version 11.1.0)
    […]
    OK    Cortex-M33 hard runs on mps2-an505 with arm-none-eabi/armv8m.main_hard_fp
    OK    default crt0, libc and builtins from arm-none-eabi/armv7em_hard_fpv4_sp_d16
    PASS (0 failed, 0 warned)

Assisted-by: claude-opus-5.5 (Claude Code)
@pmenzel Paul Menzel (pmenzel) force-pushed the compiler-rt-add-bare-metal-arm-runtimes branch from 871e3a5 to f1a9e32 Compare October 16, 2026 14:33
@pmenzel Paul Menzel (pmenzel) merged commit 40e5b38 into master Oct 16, 2026
@pmenzel Paul Menzel (pmenzel) deleted the compiler-rt-add-bare-metal-arm-runtimes branch October 16, 2026 14:36
Sign in to join this conversation on GitHub.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant